While static and maps can enliven geographic data, interactive maps can take them to a new level. Interactivity can have many different functions and forms. We can pan around and zoom into any part of a geographic dataset overlaid on a map to show the context. We can also tilt and rotate maps freely to have a better view. The release of the leaflet package in 2015 revolutionized interactive web map creation from within R and a number of packages have built on these foundations adding new features (e.g. leaflet.extras) and making the creation of web maps as simple as creating static maps. Here we’ll introduce two packages leaflet and plotly in drawing maps so as to provide powerful tools when we need to interactively processing geo-data.
leafletLeaflet is one of the most popular open-source JavaScript libraries for interactive maps. The JavaScript library actively developed at github.com/Leaflet/Leaflet.
The leaflet package creates interactive web maps in few lines of code. One of the exciting things about the package is its tight integration with the R package for interactive on-line visualisation, shiny. Here we have a quick start for those who want to draw maps in an interactive way with package leaflet.
For more information on rstudio/leaflet, see rstudio.github.io/leaflet/.
To install this R package, run this command at your R prompt:
devtools::install_github("rstudio/leaflet")
# or you can use
# install.packages("leaflet")
There are four steps to create a Leaflet map:
library(leaflet)
ex1 <- leaflet() %>%
addTiles() %>% # Add default OpenStreetMap map tiles
addMarkers(lng = -73.9626, lat = 40.8075, popup = "Columbia University")
ex1 # Print the map
There are several ways to manipulate the attributes of the map widget. Please see the help page ?setView for details.
setView() sets the center of the map view and the zoom level;fitBounds() fits the view into the rectangle [lng1, lat1] – [lng2, lat2];clearBounds() clears the bound, so that the view will be automatically determined by the range of latitude/longitude data in the map layers if provided;ex2 <- leaflet() %>%
addTiles() %>%
setView(lng = -73.9626, lat = 40.8075, zoom = 9) # set the zoom view = 9
ex2
Many popular free third-party basemaps can be added using the addProviderTiles() function, which is implemented using the leaflet-providers plugin. See here for the complete set.
ex3 <- ex1 %>% addProviderTiles(providers$Stamen.Toner)
ex3
You can also use addWMSTiles() to add WMS (Web Map Service) tiles. The map below shows the Base Reflectivity (a measure of the intensity of precipitation occurring) using the WMS from the Iowa Environmental Mesonet:
ex4 <- leaflet() %>%
addTiles() %>%
setView(-93.65, 42.0285, zoom = 4) %>%
addWMSTiles(
"http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi",
layers = "nexrad-n0r-900913",
options = WMSTileOptions(format = "image/png", transparent = TRUE),
attribution = "Weather data © 2012 IEM Nexrad"
)
ex4
If you need more than one basemap on a map, feel free to stack them by adding multiple tile layers. This generally only makes sense if the front tiles consist of semi transparent tiles, or have an adjusted opacity via the options argument.
ex5 <- ex1 %>% addProviderTiles(providers$MtbMap) %>%
addProviderTiles(providers$Stamen.TonerLines,
options = providerTileOptions(opacity = 0.35)) %>%
addProviderTiles(providers$Stamen.TonerLabels)
ex5
We can add icon markers to locate some particular places. Here we want to mark all of the subway stations in NYC, with popup as the station names.
Here, to have a better view of the where the area we are looking at is, we use addMiniMap to have a mini-map in the corner. And the square inside shows the showing part of the big map is in what part of the whole country.
subway.df <- read.csv("data/SUBWAY_STATION.csv")
print(subway.df[c(1: 5), ]) # show the data of nyc subway stations
## OBJECTID NAME the_geom x
## 1 1 Astor Pl -73.99106999861966 40.73005400028978 -73.99107
## 2 2 Canal St -74.00019299927328 40.71880300107709 -74.00019
## 3 3 50th St -73.98384899986625 40.76172799961419 -73.98385
## 4 4 Bergen St -73.97499915116808 40.68086213682956 -73.97500
## 5 5 Pennsylvania Ave -73.89488591154061 40.66471445143568 -73.89489
## y
## 1 40.73005
## 2 40.71880
## 3 40.76173
## 4 40.68086
## 5 40.66471
ex6 <- leaflet(subway.df) %>%
addTiles() %>%
addMarkers(~x, ~y, popup = ~as.character(NAME)) %>%
setView(lng = -73.9626,lat = 40.8075,zoom = 14) %>% # set the zoom view = 14
addMiniMap()
ex6
For various purposes we can also customize their color, radius, stroke, opacity, etc. Leaflet supports even more customizable markers using the awesome markers leaflet plugin. We can also use circle markers.
ex7 <- leaflet(subway.df) %>%
addTiles() %>%
addCircleMarkers(~x, ~y, popup = ~as.character(NAME)) %>%
setView(lng = -73.9626,lat = 40.8075,zoom = 12) # set the zoom view = 12
ex7
Popups are small boxes containing arbitrary HTML, that point to a specific point on the map. Here we show an example of Columbia University.
content <- paste(sep = "<br/>",
"<b><a href='https://www.columbia.edu'> Columbia University</a></b>",
"116th and Broadway",
"New York, NY 10027"
)
ex8 <- leaflet() %>%
addTiles() %>%
addPopups(-73.9626, 40.8075, content,
options = popupOptions(closeButton = FALSE)
)
ex8
A label is a textual or HTML content that can attached to markers and shapes to be always displayed or displayed on mouse over. Unlike popups you don’t need to click a marker/polygon for the label to be shown.
ex9 <- leaflet() %>%
addTiles() %>%
addMarkers(-73.9626, 40.8075, label = "Columbia University")
ex9
Line and polygon data can come from a variety of sources:
SpatialPolygons, SpatialPolygonsDataFrame, Polygons, and Polygon objects (from the sp package)SpatialLines, SpatialLinesDataFrame, Lines, and Line objects (from the sp package)MULTIPOLYGON, POLYGON, MULTILINESTRING, and LINESTRING objects (from the sf package)map objects (from the maps package’s map() function); use map(fill = TRUE) for polygons, FALSE for polylines(NA, NA). It is not possible to represent multi-polygons nor polygons with holes using this method; use SpatialPolygons instead.I’ll use the census data, and filter those states in the east coast. Here we add a polygons in the border of each state. Also we add a highlighting shapes to emphasize the currently moused-over polygon. We can also use other commands addCircles(), addRectangles() to add shapes.
For legend, usually we use the addLegend function to add a legend. The easiest way to use addLegend is to provide pal (a palette function, as generated from colorNumeric et al.) and values, and let it calculate the colors and labels for you.
library(sp)
library(tidyverse)
library(raster)
library(rgdal)
# From https://www.census.gov/geo/maps-data/data/cbf/cbf_state.html
states <- readOGR("data/cb_2017_us_state_20m/cb_2017_us_state_20m.shp",
layer = "cb_2017_us_state_20m", GDAL1_integer64_policy = TRUE)
## OGR data source with driver: ESRI Shapefile
## Source: "/Users/youki/Documents/columbia/courses/5702EDAV/How to draw beautiful maps in R/data/cb_2017_us_state_20m/cb_2017_us_state_20m.shp", layer: "cb_2017_us_state_20m"
## with 52 features
## It has 9 fields
## Integer64 fields read as doubles: ALAND AWATER
neStates <- subset(states, states$STUSPS %in% c(
"ME", "VT", "NH", "MA", "RI", "CT", "NY", "NJ", "FL",
"PA", "DE", "MD", "WV", "VA", "NC", "SC", "GA", "AL"
)) # choose the states in the east coast
palette <- leaflet::colorBin("YlOrRd", domain = neStates$ALAND)
ex10 <-leaflet(neStates) %>%
addTiles() %>%
addPolygons(color = "#444444", weight = 1, smoothFactor = 0.5,
opacity = 1.0, fillOpacity = 0.5,
fillColor = ~palette(ALAND),
highlightOptions = highlightOptions(color = "white", weight = 2,
bringToFront = TRUE)) %>%
addLegend(pal = palette, values = ~ALAND, position="bottomright",
title = "2017 US Land Areas (East Coast)")
ex10
plotlyPlotly’s R graphing library makes interactive, publication-quality graphs online. Here we give examples and instructions of some very fancy graphs.
devtools::install_github("ropensci/plotly")
# or you can directly use
# install.packages("plotly")
# Plotly is now on CRAN!
Here we want to use the same data when plotting us census plot above by leaflet, and make a comparison.
library(plotly)
census.df <- neStates@data
# create the text when mouse move to the corresponding county
census.df$hover <- with(census.df, paste(NAME, "<br>",
"AWATER", AWATER))
# give state boundaries a white border
l <- list(color = toRGB("white"), width = 2)
# specify some map projection/options
g <- list(
scope = 'usa',
projection = list(type = 'albers usa'),
showlakes = TRUE,
lakecolor = toRGB('white'))
ex11 <- plot_geo(census.df, locationmode = 'USA-states') %>%
add_trace(
z = ~ALAND, text = ~hover, locations = ~STUSPS,
color = ~ALAND, colors = 'Oranges') %>%
layout(
title = '2017 US Land Areas (East Coast)',
geo = g)
ex11
We can see that plotly’s plots have a line of small buttons in the top-right corner, with functions like download, pan, select, zoom, box select, etc. These serve as very good interactive tools. And for the grammer, it works quite similar to leaflet. And the graph shows the same result as the previous one. So feel free to choose packages when plotting choropleth maps.
If you want to show flights across or within countries in the plots, plotly provides convenient tools to visualize lines, circles or even contours. Here we give two examples.
We use the data of flights from the official tutorial of plotly, which is the data of Feb 2011. Our goal is to visualize the flight paths in the map. Here we draw circle to deonte the airports using add_markers. Also we draw lines from the start location to the end location using add_segments.
# airport locations
air <- read.csv('https://raw.githubusercontent.com/plotly/datasets/master/2011_february_us_airport_traffic.csv')
# flights between airports
flights <- read.csv('https://raw.githubusercontent.com/plotly/datasets/master/2011_february_aa_flight_paths.csv')
flights$id <- seq_len(nrow(flights))
# map projection
geo <- list(
scope = 'north america',
projection = list(type = 'azimuthal equal area'),
showland = TRUE,
landcolor = toRGB("gray95"),
countrycolor = toRGB("gray80")
)
ex12 <- plot_geo(locationmode = 'USA-states', color = I("red")) %>%
add_markers(
data = air, x = ~long, y = ~lat, text = ~airport,
size = ~cnt, hoverinfo = "text", alpha = 0.5) %>%
add_segments(
data = group_by(flights, id),
x = ~start_lon, xend = ~end_lon,
y = ~start_lat, yend = ~end_lat,
alpha = 0.3, size = I(1), hoverinfo = "none") %>%
layout(
title = 'Feb. 2011 American Airline Flight Paths',
geo = geo, showlegend = FALSE)
ex12
## Warning: `line.width` does not currently support multiple values.
Finally I’ll show the fancy 3-D interactive contour lines on globe. You can click and drag to rotate so as to have an overall view all over the world.
df <- read.csv('https://raw.githubusercontent.com/plotly/datasets/master/globe_contours.csv')
df$id <- seq_len(nrow(df))
d <- df %>%
gather(key, value, -id) %>%
separate(key, c("l", "line"), "\\.") %>%
spread(l, value)
geo <- list(
showland = TRUE,
showlakes = TRUE,
showcountries = TRUE,
showocean = TRUE,
countrywidth = 0.5,
landcolor = toRGB("grey90"),
lakecolor = toRGB("white"),
oceancolor = toRGB("white"),
projection = list(
type = 'orthographic',
rotation = list(
lon = -100,
lat = 40,
roll = 0
)
),
lonaxis = list(
showgrid = TRUE,
gridcolor = toRGB("gray40"),
gridwidth = 0.5
),
lataxis = list(
showgrid = TRUE,
gridcolor = toRGB("gray40"),
gridwidth = 0.5
)
)
ex13 <- plot_geo(d) %>%
group_by(line) %>%
add_lines(x = ~lon, y = ~lat) %>%
layout(
showlegend = FALSE, geo = geo,
title = 'Contour Lines Over Globe'
)
ex13
Reference: